package com.snail.workflow.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.snail.common.core.constant.CommonConstants;
import com.snail.common.core.utils.DateUtils;
import com.snail.common.core.utils.PageUtils;
import com.snail.common.core.utils.StringUtils;
import com.snail.common.core.web.page.PageResult;
import com.snail.common.security.utils.SecurityUtils;
import com.snail.system.api.dto.SysUserDto;
import com.snail.workflow.api.constant.WorkflowConstants;
import com.snail.workflow.api.domain.dto.HisActivityInstanceDto;
import com.snail.workflow.api.domain.dto.TaskDataDto;
import com.snail.workflow.api.domain.dto.TaskNextNodeDto;
import com.snail.workflow.api.domain.dto.TaskRecordsDto;
import com.snail.workflow.api.query.FlowTaskQuery;
import com.snail.workflow.factory.FeignFactoryService;
import com.snail.workflow.factory.FlowServiceFactory;
import com.snail.workflow.mapper.FlowTaskMapper;
import com.snail.workflow.service.IFlowTaskService;
import com.snail.workflow.utils.WorkflowUtils;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.impl.persistence.entity.CommentEntityImpl;
import org.flowable.task.api.Task;
import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntityImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @Description: 流程任务业务实现
 * @Author: Snail
 * @CreateDate: 2022/7/26 22:59
 * @Version: V1.0
 */
@Service
public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService {

    @Autowired
    private FeignFactoryService feignFactoryService;

    @Autowired
    private FlowTaskMapper flowTaskMapper;

    /**
     * 查询待办
     *
     * @param flowTaskQuery 查询参数
     * @return
     */
    @Override
    public PageResult<TaskDataDto> findTodo(FlowTaskQuery flowTaskQuery) {
        Page<TaskDataDto> page = PageUtils.buildPage(flowTaskQuery);
        page = flowTaskMapper.findTodo(page, flowTaskQuery);
        // 当前处理人和流程发起人
        return PageUtils.pageResult(page);
    }

    /**
     * 获取当前任务的下一节点信息
     *
     * @param task 当前任务
     * @return 下一节点信息
     */
    @Override
    public List<TaskNextNodeDto> getNextNode(Task task, String type) {
        List<TaskNextNodeDto> nextNodeDtos = new ArrayList<>();
        //获取当前流程所有流程变量(网关节点时需要校验表达式)
        Map<String, Object> variables = taskService.getVariables(task.getId());
        // 获取所有节点
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        //获取当前节点的下一节点信息
        List<FlowElement> flowElements = WorkflowUtils.findNextElements(task.getTaskDefinitionKey(), bpmnModel, type, variables);
        //封装节点参数
        for (FlowElement flowElement : flowElements) {
            //开始节点
            if (flowElement instanceof StartEvent) {
                StartEvent startEvent = (StartEvent) flowElement;
                TaskNextNodeDto dto = new TaskNextNodeDto();
                dto.setNextNodeKey(startEvent.getId());
                dto.setNextNodeName(startEvent.getName());
                nextNodeDtos.add(dto);
            }
            //用户任务
            if (flowElement instanceof UserTask) {
                nextNodeDtos.add(buildNextUserTaskNode(flowElement, variables));
            }
            //结束节点
            if (flowElement instanceof EndEvent) {
                EndEvent endEvent = (EndEvent) flowElement;
                TaskNextNodeDto dto = new TaskNextNodeDto();
                dto.setIsEndNode("Y");
                dto.setNextNodeKey(endEvent.getId());
                dto.setNextNodeName(endEvent.getName());
                nextNodeDtos.add(dto);
            }
        }
        return nextNodeDtos;
    }

    /**
     * 封装下一用户任务节点
     *
     * @param flowElement
     */
    private TaskNextNodeDto buildNextUserTaskNode(FlowElement flowElement, Map<String, Object> variables) {
        // 下一节点信息
        TaskNextNodeDto dto = new TaskNextNodeDto();
        // 节点key
        dto.setNextNodeKey(flowElement.getId());
        // 节点名称
        dto.setNextNodeName(flowElement.getName());
        // 用户任务
        UserTask userTask = (UserTask) flowElement;
        // 用户类型
        String userType = userTask.getAttributeValue(WorkflowConstants.NAMASPASE, WorkflowConstants.PROCESS_CUSTOM_USER_TYPE);
        // 组织过滤类型
        String filterOrgType = userTask.getAttributeValue(WorkflowConstants.NAMASPASE, WorkflowConstants.PROCESS_CUSTOM_ORG_TYPE);
        dto.setOrgFilterType(StringUtils.defaultString(filterOrgType,WorkflowConstants.FILTER_ORG_TYPE_1));
        // 组织过滤
        String filterOrgIds = userTask.getAttributeValue(WorkflowConstants.NAMASPASE, WorkflowConstants.PROCESS_CUSTOM_ORG_IDS);
        if(StringUtils.isNotEmpty(filterOrgIds)){
            dto.setFilterOrgIds(Arrays.asList(filterOrgIds.split(CommonConstants.SPLIT_CHAR_1)));
        }
        //会签
        if (userTask.hasMultiInstanceLoopCharacteristics()) {
            dto.setMultiInstance("Y");
        }
        // 指定流程发起人
        if (StringUtils.equalsIgnoreCase(WorkflowConstants.INITIATOR, userType)) {
            // 审批人
            String assignee = variables.get(WorkflowConstants.INITIATOR).toString();
            dto.setDefaultApprove("Y");
            dto.setUserIds(Collections.singletonList(assignee));
        }
        // 指定审批人
        else if (StringUtils.equalsIgnoreCase(WorkflowConstants.USER_TYPE_ASSIGNEE, userType)) {
            // 审批人
            String assignee = userTask.getAssignee();
            dto.setDefaultApprove("Y");
            dto.setUserIds(Collections.singletonList(assignee));
        }
        // 指定待审批人
        else if (StringUtils.equalsIgnoreCase(WorkflowConstants.USER_TYPE_USERS, userType)) {
            List<String> candidateUsers = userTask.getCandidateUsers();
            dto.setUserIds(candidateUsers);
        }
        // 指定待审批组（角色）
        else if (StringUtils.equalsIgnoreCase(WorkflowConstants.USER_TYPE_GROUPS, userType)) {
            List<String> candidateGroups = userTask.getCandidateGroups();
            dto.setRoleIds(candidateGroups);
        }
        return dto;
    }

    @Override
    public PageResult<TaskDataDto> findDone(FlowTaskQuery flowTaskQuery) {
        Page<TaskDataDto> page = PageUtils.buildPage(flowTaskQuery);
        flowTaskQuery.setUserId(SecurityUtils.getUserId().toString());
        page = flowTaskMapper.findDone(page, flowTaskQuery);
        // 当前处理人和流程发起人
        return PageUtils.pageResult(page);
    }

    private PageResult<TaskDataDto> buildResultUser(Page<TaskDataDto> page) {
        page.getRecords().forEach(item -> {
            SysUserDto assignee = feignFactoryService.getUserInfoById(item.getAssigneeId());
            item.setAssigneeName(assignee.getNickName());
            item.setAssigneeDeptName(assignee.getDept().getDeptFullName());
            SysUserDto startUser = feignFactoryService.getUserInfoById(item.getStartUserId());
            item.setStartUserName(startUser.getNickName());
            item.setStartDeptName(startUser.getDept().getDeptFullName());
            item.setBizUrl(item.getBizUrl() + "&taskUserId=" + item.getAssigneeId());
        });
        return PageUtils.pageResult(page);
    }

    @Override
    public List<TaskRecordsDto> findRecords(String bizId) {
        List<TaskRecordsDto> recordsDtos = flowTaskMapper.findRecords(bizId);
        recordsDtos.forEach(item -> {
            item.setDuration(DateUtils.getDurationStr(item.getDurationTime()));
            if (StringUtils.isNotEmpty(item.getAssigneeId())) {
                SysUserDto sysUser = feignFactoryService.getUserInfoById(item.getAssigneeId());
                item.setAssigneeName(sysUser.getNickName());
                item.setAssigneeDeptName(sysUser.getDept().getDeptFullName());
            }
            if(item.getTaskFinishTime() == null){
                item.setComment(null);
            }
        });
        return recordsDtos;
    }

    @Override
    public EndEvent getEndEventNode(Task task) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        Process mainProcess = bpmnModel.getMainProcess();
        List<EndEvent> elements = mainProcess.findFlowElementsOfType(EndEvent.class, false);
        if (elements == null) {
            return null;
        }
        return elements.get(0);
    }

    @Override
    public void insertBatchHisActInstance(List<HisActivityInstanceDto> dtoList) {
        this.flowTaskMapper.insertBatchHisActInstance(dtoList);
    }

    @Override
    public Task getTaskByInsId(String instanceId, String userId) {
        return taskService.createTaskQuery().processInstanceId(instanceId)
                .taskAssignee(userId)
                .singleResult();
    }

    @Override
    public void insertHisTaskInst(HistoricTaskInstanceEntityImpl historicTaskInstance) {
        flowTaskMapper.insertHisTaskInst(historicTaskInstance);
    }

    @Override
    public void saveComment(CommentEntityImpl commentEntity) {
        flowTaskMapper.saveComment(commentEntity);
    }

    @Override
    public void insertBatchHisTaskInst(List<HistoricTaskInstanceEntityImpl> dtoList) {
        flowTaskMapper.insertBatchHisTaskInst(dtoList);
    }

    @Override
    public boolean checkCurrentToTargetElement(String sourceElementKey, String targetElementKey, String processInstanceId) {
        // 获取所有节点
        String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        return WorkflowUtils.checkCurrentToTargetElement(sourceElementKey, targetElementKey, bpmnModel.getProcesses());
    }

    @Override
    public List<String> getCommonUseComment(String userId, String type) {
        return flowTaskMapper.getCommonUseComment(userId, type);
    }

}
